package com.yungu.swift.api.utils;


import com.yungu.swift.utils.Base64Utils;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;


/**
 * Created by Hello on 2018/11/8.
 */

public class RsaUtil {

    public static final String KEY_ALGORTHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

    //公钥
    public static final String PUBLIC_KEY = "RSAPublicKey";
    //私钥
    public static final String PRIVATE_KEY = "RSAPrivateKey";

    //RSA密钥大小
    public static final int KEY_SIZE = 1024;
    //RSA最大解密密文大小
    public static final int MAX_DECRYPT_BLOCK = 128;
    //RSA最大加密明文大小
    public static final int MAX_ENCRYPT_BLOCK = 117;

    //移动端公钥
    public static final String MOBILE_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq2vsKP6R8+v1StMIB7aOY52fi\n" +
            "/m1Eje8olYlj+bb7TCuyq28mhdtgAzP957vT6Edq3Kx7jhZSRWzgVyX8ZxDOntcD\n" +
            "lCgMU/N8cxb+M1E6ZwkCztUpx3B+p57oP/IQ3FU4yo738lsNAtRvze87NqJlokqF\n" +
            "NRgxhzqaqSD56xIKWQIDAQAB";

    //服务端私钥
    public static final String SERVER_PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALRt1EJpTRfHCs3x\n" +
            "cFErNJgwNQWHW1wHhV0bV5FeHKX4J0l+ZAYuqLj+toAr8WAI7GqVVxNfro66Ctlt\n" +
            "f8CPPSB66mfuNqx1WEBLIvPg6pijbVnFZJf2VAeP+sgSqkOx8tw1Jibt3IjLpBVp\n" +
            "JHSzcjXx0bRy5zX6eiPM6dTuInEtAgMBAAECgYBDJokT+mrVyZ9szEnX+7OEXJSv\n" +
            "LRREaQwqfc2WbadN4dCFWsPRRTZIO4wiobgg3NFYTaJttHOb7QOyrj0YpnbB6hf8\n" +
            "t92VY+5g4ZXPRjXl5aGp6G6minc4mNpysZN3aM2o3gPzsPqtGCqhEyJvb9C+4bZ7\n" +
            "VHoN/hNcrhZrvvEVAQJBAO1wL9M0BTQ3uoW7qmtpnXFTDQM2GQybGHBe3SeLtODL\n" +
            "SXPn6Yl7JdWGNNR7XPsp/3nJP3J+ekSiw0tD7dHQNkECQQDCiLnlhW/v0ck0yWxI\n" +
            "jz5k2ps+ge0YaN9WUpFxS10zfOK0LYtVbkOUuvFIJEY+mpNUbYpZqQkKhEZYtBAt\n" +
            "KHftAkEAlg5Siz3TFB8sKBxkJaIHuHjuPltQFWFnXoz1Y2qtk2umLjQgqZxEWt+I\n" +
            "P8j/CFSQep0vFeLMzte2aLizcegOgQJBAKyhhMZnydP6SSGs9YjWhToevNGVnv+g\n" +
            "YUFLeIIfrQ+57byTBijD9vkPhHrOpf98UO4FX1b9qGZKx9qDwC8y0GkCQD8zL1V2\n" +
            "rOrbx5XXHUy2P+qtM55YX5gv3tpp6EBZEinSFFxd2AjRDjmhquzCP4JlzlhrItQ7\n" +
            "G/P6OGt2A5CBDj4=";

    public static final String PUBLIC_KEY_DEFAULT = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHppRtQ75DyRCRE0EBWFKG8TDLLjrzDj5Uejy2\n" +
            "kbAhLJI8CB9HjnSe0gNjSjMsRuYHv2ai8bf+1B3N1LoKAA7nRi8vlYZUu04wCkD3n5bsrGCSjkI+\n" +
            "Hobbkw9cuDWdn/INFguLpx4MT1s4PfIlRT/QDzCMHH9zbtu04SjY/HeHFQIDAQAB";

    public static final String PRIVATE_KEY_DEFAULT = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMemlG1DvkPJEJETQQFYUobxMMsu\n" +
            "OvMOPlR6PLaRsCEskjwIH0eOdJ7SA2NKMyxG5ge/ZqLxt/7UHc3UugoADudGLy+VhlS7TjAKQPef\n" +
            "luysYJKOQj4ehtuTD1y4NZ2f8g0WC4unHgxPWzg98iVFP9APMIwcf3Nu27ThKNj8d4cVAgMBAAEC\n" +
            "gYAJO8wLjaGEEw2AGCrefA49DXSbQQUa7qK1V33tV6mUrzIIMrv3nEAKhF4BvpHFT3YeD50YliNT\n" +
            "Vs4WEeuUwNHvjWWbUJz+qbs9H7IfF2Rvfwn2C1fsh9EO3N/PuFGqjKoJ1ABkV0n01eEdNE+YMFFa\n" +
            "YbRANP+ihYwpeLQkSAuDlQJBAOdKYq9nQdyGKFu387OTxMYja/Zclt5EiWMdM6q4Uu0o+5xZxKYs\n" +
            "4cpOBEUQ85mLYDCEPKpFs7Us3sxHCXJPoicCQQDc+t5yuNpjpWnUmzE73hZKn7WPM0iCFq6XxodM\n" +
            "t7/m2RL+Bov+ITayKGRqqkCgrd/cFfpfkrn3c0o56JGdQt5jAkEAqzwfI81GrYWFi6H9Aw57uFWx\n" +
            "MIwMBjKnpIGSJuqgOoUFAxRSytSfta9c6eVK9HqDlGfSoh1IlvHJYWOWUtMyMQJAflpPkWdTMEDn\n" +
            "x3JJb4LoWhkB9nSLVB7mC9CvklVKvBOJAYj6yP9yx+HCsR7SP03MP4qtk7JQ9UWNvpjVkgcnIwJB\n" +
            "AMEYXChWLP5to8IMr6RIrxxiAX3VQEwkY/Gscf7iPHZmFbjpZhW6EQD65tX7/DMquHcgarx8+K2t\n" +
            "vEKfrR7Gsek=";

    /**
     * 初始化密钥
     *
     * @return
     * @throws Exception
     */
    public static Map<String, Object> initKey() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORTHM);
        keyPairGenerator.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        //公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        //私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<String, Object>(2);
        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);

        return keyMap;
    }

    /**
     * 取得公钥，并转化为String类型
     *
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return Base64Utils.encode(key.getEncoded());
    }

    /**
     * 取得私钥，并转化为String类型
     *
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return Base64Utils.encode(key.getEncoded());
    }


    /**
     * 用私钥加密
     *
     * @param data 加密数据
     * @param key  密钥
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
        //解密密钥
        byte[] keyBytes = Base64Utils.decode(key);
        //取私钥
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);

        //对数据加密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);

        return cipherDoFinal(cipher, data, MAX_ENCRYPT_BLOCK);
    }

    /**
     * 用私钥解密
     *
     * @param data 解密数据
     * @param key  密钥
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {
        //对私钥解密
        byte[] keyBytes = Base64Utils.decode(key);

        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        //对数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, privateKey);

        return cipherDoFinal(cipher, data, MAX_DECRYPT_BLOCK);
    }

    /**
     * 用公钥加密
     *
     * @param data 加密数据
     * @param key  密钥
     * @return
     * @throws Exception
     */
    public static byte[] encryptByPublicKey(byte[] data, String key) throws Exception {
        //对公钥解密
        byte[] keyBytes = Base64Utils.decode(key);
        //取公钥
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);

        //对数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);

        return cipherDoFinal(cipher, data, MAX_ENCRYPT_BLOCK);
    }

    /**
     * 用公钥解密
     *
     * @param data 解密数据
     * @param key  密钥
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {
        //对私钥解密
        byte[] keyBytes = Base64Utils.decode(key);
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        Key publicKey = keyFactory.generatePublic(x509EncodedKeySpec);

        //对数据解密
        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, publicKey);

        return cipherDoFinal(cipher, data, MAX_DECRYPT_BLOCK);
    }

    /**
     * 用私钥对信息生成数字签名
     *
     * @param data       //加密数据
     * @param privateKey //私钥
     * @return
     * @throws Exception
     */
    public static String sign(byte[] data, String privateKey) throws Exception {
        //解密私钥
        byte[] keyBytes = Base64Utils.decode(privateKey);
        //构造PKCS8EncodedKeySpec对象
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        //指定加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        //取私钥匙对象
        PrivateKey privateKey2 = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        //用私钥对信息生成数字签名
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateKey2);
        signature.update(data);

        return Base64Utils.encode(signature.sign());
    }

    /**
     * 校验数字签名
     *
     * @param data      加密数据
     * @param publicKey 公钥
     * @param sign      数字签名
     * @return
     * @throws Exception
     */
    public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
        //解密公钥
        byte[] keyBytes = Base64Utils.decode(publicKey);
        //构造X509EncodedKeySpec对象
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        //指定加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        //取公钥匙对象
        PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec);

        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(publicKey2);
        signature.update(data);
        //验证签名是否正常
        return signature.verify(Base64Utils.decode(sign));

    }


    /**
     * 分段大小
     *
     * @param cipher
     * @param srcBytes
     * @param segmentSize
     * @return
     * @throws IOException
     */
    private static byte[] cipherDoFinal(Cipher cipher, byte[] srcBytes, int segmentSize) throws IOException, BadPaddingException, IllegalBlockSizeException {
        if (segmentSize <= 0) {
            throw new RuntimeException("分段大小必须大于0");
        }
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int inputLen = srcBytes.length;
        int offSet = 0;
        byte[] cache;
        int i = 0;
        // 对数据分段解密
        while (inputLen - offSet > 0) {
            if (inputLen - offSet > segmentSize) {
                cache = cipher.doFinal(srcBytes, offSet, segmentSize);
            } else {
                cache = cipher.doFinal(srcBytes, offSet, inputLen - offSet);
            }
            out.write(cache, 0, cache.length);
            i++;
            offSet = i * segmentSize;
        }
        byte[] data = out.toByteArray();
        out.close();
        return data;
    }

    /**
     * 公钥加密-外部调用
     *
     * @param str
     * @return
     */
    public static String encryptByPublic(String str, String publicKey) {
        try {
            return Base64Utils.encode(encryptByPublicKey(str.getBytes("UTF-8"), publicKey));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 公钥解密-外部调用
     *
     * @param key
     * @return
     */
    public static String decryptByPublic(String key, String publicKey) {
        try {
            return new String(decryptByPublicKey(Base64Utils.decode(key), publicKey), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 私钥加密-外部调用
     *
     * @param str
     * @return
     */
    public static String encryptByPrivate(String str, String privateKey) {
        try {
            return Base64Utils.encode(encryptByPrivateKey(str.getBytes("UTF-8"), privateKey));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 私钥解密-外部调用
     *
     * @param key
     * @return
     */
    public static String decryptByPrivate(String key, String privateKey) {
        try {
            return new String(decryptByPrivateKey(Base64Utils.decode(key), privateKey), "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String doUrlAndEncrypt(String jsonstr) {
        try {
            String rsaStr = RsaUtil.encryptByPublic(jsonstr, RsaUtil.MOBILE_PUBLIC_KEY);
            return URLEncoder.encode(rsaStr, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
            return "InterceptorError\n" + e.getMessage();
        }
    }

}

